package com.yyd.util.redishelper;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.api.options.KeysScanOptions;
import org.redisson.api.redisnode.RedisCluster;
import org.redisson.api.redisnode.RedisNodes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.time.Duration;
import java.util.*;

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private RedissonClient redissonClient;

    private static final Integer bigKeyThreshold = 203;

    private static final Integer topN = 100;

    private static final Integer defaultKeyPrefixLength = 30;

    private static final Integer defaultScanCount = 50000;

    private static final Random RANDOM = new Random();

    private static final Integer DEFAULT_MIN_PREFIX_LENGTH = 10;

//    @RequestMapping("/generateKeys")
    public void generateKeys(@RequestParam(required = false, defaultValue = "100000")Integer  count) {
        for (int i = 0; i < count; i++){
            int randomInt = RANDOM.nextInt(5);
            if(randomInt < 3){
                redissonClient.getBucket("notExpire" + i).setAsync(1);
            }else {
                redissonClient.getBucket("expire" + i).setAsync(1, Duration.ofDays(1));
            }
        }
    }


    /**
     * 尝试解决memSize获取问题
     */
    public void a(){
        int slot = redissonClient.getKeys().getSlot("");
        RedisCluster redisNodes = redissonClient.getRedisNodes(RedisNodes.CLUSTER);



    }


    @PostMapping("/randomExpire")
    public void randomExpire(@RequestParam String prefix,
                             @RequestParam(required = false) Integer minPrefixLength,
                             @RequestParam Integer minExpireSeconds,
                             @RequestParam Integer maxExpireSeconds){
        if(prefix.contains("*") || prefix.contains("?")){
            throw new RuntimeException(String.format("pattern不能为 [%s]", prefix));
        }
        if(minPrefixLength == null){
            minPrefixLength = DEFAULT_MIN_PREFIX_LENGTH;
        }
        if(prefix.length() < minPrefixLength){
            throw new RuntimeException(String.format("前缀最小长度为 %s", minPrefixLength));
        }
        Random random = new Random();
        KeysScanOptions options = KeysScanOptions
                .defaults()
                .pattern(prefix + "*");
        Iterator<String> iterator = redissonClient.getKeys().getKeys(options).iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            int expireSeconds = random.nextInt(maxExpireSeconds - minExpireSeconds + 1) + minExpireSeconds;
            redissonClient.getBucket(next).expire(Duration.ofSeconds(expireSeconds));
            log.info("设置 {} 过期时间 {} 秒", next, expireSeconds);
        }
    }

    @RequestMapping("/notExpiredTopN")
    public void notExpiredTopN(@RequestParam(required = false) Integer scanCount,
                               @RequestParam(required = false) Integer expireKeySize,
                               @RequestParam(required = false) Integer scanMaxCount) {
        if (scanCount == null) {
            scanCount = defaultScanCount;
        }
        if(expireKeySize == null){
            expireKeySize = defaultScanCount / 10;
        }
        if(expireKeySize == 0){
            expireKeySize = 20;
        }
        int scanTotalCount = 0;
        List<String> delete = new ArrayList<>(100);
        // 获取所有keys
        Iterator<String> keys = redissonClient.getKeys().getKeys(scanCount).iterator();
        List<String> tempKeys = new ArrayList<>();
        while(keys.hasNext()){
            if(scanMaxCount != null && scanTotalCount > scanMaxCount){
                log.info("本次总共遍历 {}",  scanTotalCount);
                break;
            }
            String key = keys.next();
            tempKeys.add(key);
            scanTotalCount++;
            if(tempKeys.size() >= expireKeySize){
                batchGetTTLWithRedisson(tempKeys);
                log.debug("批量获取过期时间");
                tempKeys.clear();
            }
            if(key.startsWith("stock:activity:20241129:stage:stage1:")
                    || key.startsWith("stock:activity:20241128:stage:stage1:")
                    || key.startsWith("activity:20241128:stage:stage1:")
                    || key.startsWith("activity:20241129:stage:stage1:")
                    || key.startsWith("event:trigger:count:daily:")
            ){
                delete.add(key);
                if(delete.size() > 100){
                    if(log.isDebugEnabled()){
                        log.debug("delete {}", delete);
                    }
                    redissonClient.getKeys().delete(delete.toArray(new String[0]));
                    delete.clear();
                }
            }
        }
        if(!tempKeys.isEmpty()){
            batchGetTTLWithRedisson(tempKeys);
        }
        if(!delete.isEmpty()){
            if(log.isDebugEnabled()){
                log.debug("delete {}", delete);
            }
            redissonClient.getKeys().delete(delete.toArray(new String[0]));
            delete.clear();
        }
    }

    public Map<String, Long> batchGetTTLWithRedisson(List<String> keys) {
        Map<String, Long> result = new HashMap<>();
        // 创建批量操作批次
        RBatch batch = redissonClient.createBatch();
        // 批量添加TTL命令
        for (String key : keys) {
            batch.getKeys().remainTimeToLiveAsync(key);
        }
        // 执行批量操作
        BatchResult<?> batchResult = batch.execute();
        for(int i = 0; i < batchResult.getResponses().size(); i++){
            Object response = batchResult.getResponses().get(i);
            Long expire = null;
            if(response instanceof Long){
                expire = Long.parseLong(response.toString());
                if(Objects.equals(-1L, expire)){
                    log.info(keys.get(i));
                }
            }else {
                log.error("other response {}",  response);
            }
        }
        return result;
    }

    private long batchGetMemory(List<String> bufferKeys) {
        long totalMemoryUsage = 0L;
        RBatch batch = redissonClient.createBatch();
        // 批量添加TTL命令
        for (String key : bufferKeys) {
            batch.getBucket(key).sizeInMemoryAsync();
        }
        // 执行批量操作
        BatchResult<?> batchResult = batch.execute();
        for (int i = 0; i < batchResult.getResponses().size(); i++){
            Object response = batchResult.getResponses().get(i);
            if(response instanceof Long){
                long sizeInMemory = Long.parseLong(response.toString());
                totalMemoryUsage = totalMemoryUsage + sizeInMemory;
            }else {
                log.error("key {} 无法计算内存占用", bufferKeys.get(i));
            }
        }
        return totalMemoryUsage;
    }

    @GetMapping("/memory")
    public long memoryUsage(@RequestParam String prefix,
                            @RequestParam(required = false, defaultValue = "500") Integer scanCount,
                            @RequestParam(required = false, defaultValue = "50") Integer memoryBatchSize){
        long totalMemoryUsage = 0L;
        List<String> bufferKeys = new ArrayList<>(100);
        Iterable<String> keys = redissonClient.getKeys().getKeys(scanCount);
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if(!next.startsWith(prefix)){
                continue;
            }
            bufferKeys.add(next);
            if(bufferKeys.size() >= memoryBatchSize){
                long thisBatchMemoryUsage = batchGetMemory(bufferKeys);
                totalMemoryUsage = totalMemoryUsage + thisBatchMemoryUsage;
                log.info("该批次计算[{}]个key的内存占用[{}]", bufferKeys.size(), totalMemoryUsage);
                bufferKeys.clear();
            }
        }
        if(!bufferKeys.isEmpty()){
            long thisBatchMemoryUsage = batchGetMemory(bufferKeys);
            totalMemoryUsage = totalMemoryUsage + thisBatchMemoryUsage;
            bufferKeys.clear();
        }
        log.info("{} 开头的key 内存占用 {} Bytes", prefix,  totalMemoryUsage);
        return totalMemoryUsage;
    }



    /**
     * ssssxxxx
     * zzxxcv
     * dfscvv123
     * ssssxxxx
     * saas-center-item-prod
     */
    @RequestMapping("/topN")
    public void topN(@RequestParam(required = false) Integer maxKeyPrefixLengthParam,
                     @RequestParam(required = false) Integer scanCount,
                     @RequestParam(required = false) Integer maxBigKeyThreshold,
                     @RequestParam(required = false) Integer n,
                     @RequestParam(required = false) String prefix) {
        if(maxKeyPrefixLengthParam == null){
            maxKeyPrefixLengthParam = defaultKeyPrefixLength;
        }
        if(scanCount == null){
            scanCount = defaultScanCount;
        }
        if(maxBigKeyThreshold == null){
            maxBigKeyThreshold = bigKeyThreshold;
        }
        if(n == null){
            n = topN;
        }
        Map<String, Integer> bigKeyMap = new HashMap<>(256);
        Map<String, Integer> map = new HashMap<>((int)Math.pow(2, 25));
        Iterable<String> keys = redissonClient.getKeys().getKeys(scanCount);
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if(prefix != null && !prefix.isEmpty() && !next.startsWith(prefix)){
                continue;
            }
            int keyPrefixLength = Math.min(maxKeyPrefixLengthParam, next.length() - 1);
            String keyPrefix = next.substring(0, keyPrefixLength);
            Integer count = map.get(keyPrefix);
            if(count == null){
                count = 1;
            }else {
                count = count + 1;
            }
            map.put(keyPrefix, count);
            if(count > maxBigKeyThreshold){
                Integer logCount = bigKeyMap.get(keyPrefix);
                if(logCount == null){
                    log.info("一个完整的key例子 {}， 这种前缀的key {}， 已经超过 {} 个", next, keyPrefix, maxBigKeyThreshold);
                    bigKeyMap.put(keyPrefix, 1);
                }else if(logCount < 3){
                    log.info("一个完整的key例子 {}， 这种前缀的key {}， 已经超过 {} 个", next, keyPrefix, maxBigKeyThreshold);
                    bigKeyMap.put(keyPrefix, logCount + 1);
                }
            }
        }
        List<Map.Entry<String, Integer>> sortList = sort(map.entrySet());

        n = Math.min(n, sortList.size());
        for(int i = 0; i < n; i++){
            log.info("topN {} {}", i, sortList.get(i).toString());
        }
        log.info("sortList size : {}", sortList.size());
    }

    public static List<Map.Entry<String, Integer>> sort(Set<Map.Entry<String, Integer>> entrySet){
        List<Map.Entry<String, Integer>> list = new ArrayList<>(entrySet);
        list.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
        return list;
    }
}
